package cn.iocoder.yudao.module.digital.service.cardkeyqueuein;

import cn.hutool.core.util.IdUtil;
import cn.iocoder.yudao.module.digital.dal.dataobject.cardkeyqueuein.CardkeyQueueInDO;
import cn.iocoder.yudao.module.digital.dal.mysql.cardkeyqueuein.CardkeyQueueInMapper;
import cn.iocoder.yudao.module.digital.enums.cardkeyqueue.CardkeyInQueueStatusEnum;
import cn.iocoder.yudao.module.digital.enums.cardkeysecret.CardKeySourceEnum;
import cn.iocoder.yudao.module.digital.framework.cardkey.config.CardKeyConfig;
import cn.iocoder.yudao.module.digital.service.cardchannel.ICardChannelManager;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.digital.enums.ErrorCodeConstants.CARDKEY_QUEUE_IN_NOT_EXISTS;

/**
 * 卡密in队列 Service 实现类
 *
 * @author 超管
 */
@Service
@Validated
public class CardkeyQueueInServiceImpl implements CardkeyQueueInService {

    @Resource
    private CardkeyQueueInMapper cardkeyQueueInMapper;
    @Resource
    private CardKeyConfig cardKeyConfig;

    private void validateCardKeyQueueInExists(String id) {
        if (cardkeyQueueInMapper.selectById(id) == null) {
            throw exception(CARDKEY_QUEUE_IN_NOT_EXISTS);
        }
    }

    @Override
    public CardkeyQueueInDO getCardKeyQueueIn(String id) {
        final CardkeyQueueInDO queue = cardkeyQueueInMapper.selectById(id);
        if (queue == null) {
            throw exception(CARDKEY_QUEUE_IN_NOT_EXISTS);
        }
        return queue;
    }

    @Override
    public long countQueue(Collection<CardkeyInQueueStatusEnum> inStatusList, String productNo, Integer buyDenomination) {
        final Set<String> inStatusSet = inStatusList.stream().map(v -> v.getValue()).collect(Collectors.toSet());

        final LambdaQueryWrapper<CardkeyQueueInDO> wrapper = Wrappers.lambdaQuery();
        wrapper.in(CardkeyQueueInDO::getInStatus, inStatusSet);
        wrapper.eq(CardkeyQueueInDO::getProductNo, productNo);
        wrapper.eq(CardkeyQueueInDO::getBuyDenomination, buyDenomination);
        return cardkeyQueueInMapper.selectCount(wrapper);
    }

    @Override
    public void createCardKeyInQueue(String orderNo, Long userId, CardKeySourceEnum source, String productNo,
                                     String currencyCode, Integer buyDenomination, Integer replenishingStock) {
        final List<CardkeyQueueInDO> queueList = new ArrayList<>();
        final String rootObjectId = IdUtil.objectId();

        for (Integer i = 0; i < replenishingStock; i++) {
            // 获取卡密订单id
            final String cardOrderId = genCardOrderId(source, rootObjectId);

            final CardkeyQueueInDO queue = new CardkeyQueueInDO();
            queue.setId(IdUtil.objectId());
            queue.setSource(source.getValue());
            queue.setProductNo(productNo);
            queue.setCurrencyCode(currencyCode);
            queue.setBuyDenomination(buyDenomination);
            queue.setOrderNo(orderNo);
            queue.setUserId(userId);
            queue.setCardOrderId(cardOrderId);
            queue.setInStatus(CardkeyInQueueStatusEnum.INIT.getValue());
            queue.setInStatusUpdateTime(LocalDateTime.now());
            queueList.add(queue);
        }

        cardkeyQueueInMapper.insertBatch(queueList);
    }

    // 获取卡密订单id
    private String genCardOrderId(CardKeySourceEnum source, String rootObjectId) {
        // 是否支持聚合订单
        final Boolean supportAggregatedOrder = cardKeyConfig.getChannelDifference().get(source.getKey()).getAggregatedOrder();
        if (supportAggregatedOrder) {
            // 支持聚合订单，cardOrderId等待在定时任务中分配
            return null;
        }

        final Boolean supportsMultipleQuantitiesOrder = cardKeyConfig.getChannelDifference().get(source.getKey()).getMultipleQuantitiesOrder();
        if (supportsMultipleQuantitiesOrder) {
            // 支持单商品多数量订单, 所有队列都是同一个订单
            return rootObjectId;
        } else {
            // 不支持单商品多数量订单，每个队列都是单独1个订单
            final String cardKeyOrderId = IdUtil.objectId();
            return cardKeyOrderId;
        }
    }

    @Override
    public List<CardkeyQueueInDO> getCardKeyInQueueByStatus(Collection<CardkeyInQueueStatusEnum> inStatusList) {
        final Set<String> inStatusSet = inStatusList.stream().map(v -> v.getValue()).collect(Collectors.toSet());

        final LambdaQueryWrapper<CardkeyQueueInDO> wrapper = Wrappers.lambdaQuery();
        wrapper.in(CardkeyQueueInDO::getInStatus, inStatusSet).orderByAsc(CardkeyQueueInDO::getId);
        final List<CardkeyQueueInDO> queueList = cardkeyQueueInMapper.selectList(wrapper);
        return queueList;
    }

    @Override
    public List<CardkeyQueueInDO> getCardKeyInQueueByStatus(CardKeySourceEnum source, Collection<CardkeyInQueueStatusEnum> inStatusList) {
        final Set<String> inStatusSet = inStatusList.stream().map(v -> v.getValue()).collect(Collectors.toSet());

        final LambdaQueryWrapper<CardkeyQueueInDO> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(CardkeyQueueInDO::getSource, source.getValue());
        wrapper.in(CardkeyQueueInDO::getInStatus, inStatusSet).orderByAsc(CardkeyQueueInDO::getId);
        final List<CardkeyQueueInDO> queueList = cardkeyQueueInMapper.selectList(wrapper);
        return queueList;
    }

    @Override
    public void updateCardKeyInQueueStatus(String id, CardkeyInQueueStatusEnum status, String errorMsg) {
        validateCardKeyQueueInExists(id);

        final CardkeyQueueInDO updateQueue = new CardkeyQueueInDO();
        updateQueue.setId(id);
        updateQueue.setInStatus(status.getValue());
        if (errorMsg == null) {
            errorMsg = "";
        }
        updateQueue.setErrorMsg(errorMsg);
        updateQueue.setInStatusUpdateTime(LocalDateTime.now());
        cardkeyQueueInMapper.updateById(updateQueue);
    }

    @Override
    public void updateCardKeyInQueueStatus(String id, CardkeyInQueueStatusEnum sourceStatus, CardkeyInQueueStatusEnum targetStatus, String errorMsg) {
        validateCardKeyQueueInExists(id);

        final CardkeyQueueInDO updateQueue = new CardkeyQueueInDO();
        updateQueue.setInStatus(targetStatus.getValue());
        if (errorMsg == null) {
            errorMsg = "";
        }
        updateQueue.setErrorMsg(errorMsg);
        updateQueue.setInStatusUpdateTime(LocalDateTime.now());

        final LambdaUpdateWrapper<CardkeyQueueInDO> wrapper = Wrappers.lambdaUpdate();
        wrapper.eq(CardkeyQueueInDO::getId, id);
        wrapper.eq(CardkeyQueueInDO::getInStatus, sourceStatus.getValue());
        cardkeyQueueInMapper.update(updateQueue, wrapper);
    }

    @Override
    public int assignCardOrderId(CardKeySourceEnum source) {
        final CardkeyQueueInDO updateQueue = new CardkeyQueueInDO();
        updateQueue.setCardOrderId(IdUtil.objectId());

        final LambdaUpdateWrapper<CardkeyQueueInDO> wrapper = Wrappers.lambdaUpdate();
        wrapper.eq(CardkeyQueueInDO::getSource, source.getValue());
        wrapper.isNull(CardkeyQueueInDO::getCardOrderId);
        wrapper.in(CardkeyQueueInDO::getInStatus, Arrays.asList(CardkeyInQueueStatusEnum.INIT.getValue(), CardkeyInQueueStatusEnum.IN_PROGRESS.getValue()));

        final int row = cardkeyQueueInMapper.update(updateQueue, wrapper);
        return row;
    }

}